/*  tBookShelf: TextPlugin
    (C) 2007  TvN (Volodymyr Tarasenko), e-mail: tvntsr@yahoo.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


#include "Utils/DebugStream.hpp"
#include "PageManager.hpp"

// :TODO: rewrite it to store unrendered pages in vector<>
// :TODO: store only rendered pages in cache

PageCacheManager::~PageCacheManager()
{
    r_pages.clear();

    for(PAGE_CACHE::iterator p = cache.begin();
        p != cache.end();
        ++p)
    {
        delete p->second;
        p->second = 0;
    }

    cache.clear();
}


void PageCacheManager::destroyPages()
{
    r_pages.clear();

    for(PAGE_CACHE::iterator p = cache.begin();
        p != cache.end();
        ++p)
    {
        delete p->second;
        p->second = 0;
    }

    cache.clear();
    current_page = 0;
    current_number = 0;
}

void PageCacheManager::storeRendered(Page* page)
{
    if (!page->isRendered())
    {
        return;
    }

    RENDERED::iterator p = std::find(r_pages.begin(), r_pages.end(), page);
    if (p == r_pages.end())
    {
        r_pages.push_back(page);
    }
}

void PageCacheManager::releasePage(Page* page)
{
    if (page != current_page)
    {
        throw std::runtime_error("Bad page");
    }

    storeRendered(page);

    if (r_pages.size() >= max_size)
        decreaseCache();

    current_page = 0;
}

void PageCacheManager::decreaseCache()
{
    if (r_pages.empty())
        return;

    Page* page = r_pages.front();

    if ((current_page)&&
        (current_page->num == page->num)&&
        (current_page->orient == page->orient))
    {
        page = r_pages.back();
        
        if ((current_page)&&
            (current_page->num == page->num)&&
            (current_page->orient == page->orient))
        {
            // Opps, do nothing
            return;
        }

        r_pages.pop_back();
    }
    else
    {
        r_pages.pop_front();
    }

    delete page->area;
    page->area = 0;
}

Page* 
PageCacheManager::createNewPage(size_t num)
{
    if (current_page)
    {
        throw std::runtime_error("Unreleased page exists");
    }
    
    PageReleaser page(new Page(num));

    PAGE_CACHE::iterator p = cache.find(num);
    if (p != cache.end())
    {
        if (p->second->isRendered())
        {
            RENDERED::iterator i =  find(r_pages.begin(), 
                                         r_pages.end(), 
                                         p->second);
            if (i != r_pages.end())
            {
                r_pages.erase(i);
            }
        }

        delete p->second;
        p->second = page.get();
    }
    else
    {
        PAGE_CACHE::value_type item(num, page.get());
        cache.insert(item);
    }

    current_page   = page.release();
    current_number = num;

    return current_page;
}

bool PageCacheManager::isPageAvailable(size_t num)
{
    PAGE_CACHE::iterator p = cache.find(num);

    return (p != cache.end());
}

Page* PageCacheManager::getCachedPage(size_t num)
{
    if (current_page)
    {
        throw std::runtime_error("Unreleased page exists");
    }

    PAGE_CACHE::iterator p = cache.find(num);
    if (p != cache.end())
    {
        current_page = p->second;
        current_number = num;
    }

    return current_page;
}

Page* PageCacheManager::getPage(size_t num)
{
    if (current_page)
    {
        throw std::runtime_error("Unreleased page exists");
    }

    Page* ret = getCachedPage(num);
    if (ret == 0)
        ret = createNewPage(num);

    return ret;
}

Page* PageCacheManager::getLastPage()
{
    if (current_page)
    {
        throw std::runtime_error("Unreleased page exists");
    }

    if (cache.empty())
        throw std::runtime_error("Page does not exists");

    PAGE_CACHE::reverse_iterator p = cache.rbegin();
    
    return p->second;
}

Page* PageCacheManager::getFirstPage()
{
    if (current_page)
    {
        throw std::runtime_error("Unreleased page exists");
    }

    if (cache.empty())
        throw std::runtime_error("Page does not exists");

    PAGE_CACHE::iterator p = cache.begin();
        
    return p->second;
}
